<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>three</title>
    <style>
        body {
            background-color: #bfe3dd;
            color: #000;
        }

        .video1 {
            top: 0;
            left: 0;
            position: fixed;
            transform: scale(0.5);
            transform-origin: 0%;
        }
    </style>

    <script type="text/javascript"></script>

</head>

<body>

    <div class="canvas">
    </div>

    <div class="video1">


        <canvas id="canvas1" width="640" height="480"></canvas>
        <video id="video" width="640" height="480" autoplay style="display: none"></video>

    </div>
    <button onclick="">点哦</button>
    <!-- step1:引入插件 -->
    <script src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
    <script src="https://unpkg.com/ml5@latest/dist/ml5.min.js" type="text/javascript"></script>
    </head>
    <script type="importmap">
  {
    "imports": {
      "three": "https://unpkg.com/three/build/three.module.js",
      "three/addons/": "https://unpkg.com/three/examples/jsm",
      "gui":"https://unpkg.com/three/examples/jsm/libs/lil-gui.module.min.js",
      "OrbitControls":"https://unpkg.com/three/examples/jsm/controls/OrbitControls.js",
        "FBXLoader":"https://unpkg.com/three/examples/jsm/loaders/FBXLoader"
    }
  }
</script>
    <script defer>
        class speechBot {

            constructor(param) {
                this.speechProgress = window.speechSynthesis
                this.speechApi = new SpeechSynthesisUtterance()
                this.speechApi.text = param.text ?? "没传入文本"
                this.speechApi.rate = param.rate ?? 1
                this.speechApi.volume = param.volume ?? 1
                this.speechApi.lang = param.lang ?? "zh-CN"
                this.speechApi.pitch = param.pitch ?? 1.5

            }
            speak = function (text) {
                // console.log(text,this.speechProgress)
                this.speechApi.text = text
                this.speechProgress.speak(this.speechApi)
            }
            pause = function () {
                this.speechProgress.pause()
            }
            resume = function () {
                // 暂停和非暂停切换
                this.speechProgress.resume()
            }
            cancel = function () {
                // 删除所有话语
                this.speechProgress.cancel()
            }
            configGet = function () {
                // console.log(this.speechProgress.getVoices())
            }
        }
        let speakbot = new speechBot({
            text: "你在挥手",
            pitch: 1,
            rate: 1,
            volume: 20,
            lang: 'zh-CN'
        })
    </script>
    <script defer>

        function main(model, actions) {

            class listener {
                constructor(param,actions) {
                    this.actions = actions
                    this.config = {
                        beginText: "小圆",
                        endText: "结束",
                        aliveTime: 3
                    }
                    this.text = ""
                    this.speechRecognition = new webkitSpeechRecognition({
                        lang: "cmn-Hans-CN"
                    })
                    let that = this
                    this.speechRecognition.onaudiostart = (event) => {
                        console.log(`onsoundstart: `, event);

                    };
                    this.speechRecognition.onaudioend = (event) => {
                        console.log(`onsoundend:`, event);
                        setTimeout(() => {
                            that.start()
                        }, 4500)
                    };
                    this.speechRecognition.onerror = (event) => {
                        console.log(`报错信息: ${event.error}`);
                        setTimeout(() => {
                            that.start()
                        }, 4500)
                    };
                }
                start() {
                    let that =this
                    console.log("begin");
                    this.speechRecognition.start()
                    this.speechRecognition.onresult = function (event) {
                        let results = event.results
                        console.log(results)
                        if (results.length > 0) {
                            for (var i = 0; i < results.length; i++) {
                                this.text = results[i][0].transcript
                                console.log(this.text)
                                if (this.text.includes("变形")) {
                                    console.log(that.actions)
                                    that.actions[16].play()
                                    let utterance1 = new SpeechSynthesisUtterance("变形成功")
                            
                                    window.speechSynthesis.speak(utterance1)
                                }
                                // document.querySelector("textarea").innerHTML = text
                            }
                        }
                    }
                    this.speechRecognition.continuous = true
                    // this.speechRecognition.start()
                }
                end() {
                    console.log("end");
                    this.speechRecognition.stop()

                }
                alive() {
                    console.log("alive")
                }
            }
            new listener({},actions).start()
            console.log(model, actions)
            let poseNet;
            let hands = [];

            let video;
            let canvas;
            let ctx;

            let loganHandle = {
                prev: {
                    x: null,
                    y: null
                },
                next: {
                    x: null,
                    y: null
                }
            }
            let distance = {
                x_distance: null,
                y_distance: null,
                allDistance: null
            }

            let loganHandleFn = (val) => {
                // 一开始的数据
                if (!loganHandle.prev.x && !loganHandle.next.x) {
                    loganHandle.prev = JSON.parse(JSON.stringify(val))
                    return
                }
                // 之后的数据
                if (loganHandle.prev.x && !loganHandle.next.x) {
                    loganHandle.next = JSON.parse(JSON.stringify(loganHandle.prev))
                    loganHandle.prev = JSON.parse(JSON.stringify(val))
                }
                // 更新数据
                if (loganHandle.prev.x && loganHandle.next.x) {
                    loganHandle.prev = JSON.parse(JSON.stringify(loganHandle.next))
                    loganHandle.next = JSON.parse(JSON.stringify(val))
                }
                if (loganHandle.prev.x && loganHandle.next.x) {
                    let x_distance = Math.pow((loganHandle.prev.x - loganHandle.next.x), 2)
                    let y_distance = Math.pow((loganHandle.prev.y - loganHandle.next.y), 2)
                    // console.log("笛卡尔距离：",x_distance,y_distance,Math.log2(x_distance+y_distance))
                    if (Math.log2(x_distance + y_distance)) {
                        // console.log("笛卡尔距离：",x_distance,y_distance,Math.log2(x_distance+y_distance))
                        distance.allDistance = Math.log2(x_distance + y_distance)

                    }
                    distance.x_distance = Math.log2(x_distance)
                    distance.y_distance = Math.log2(y_distance)

                }
            }
            // loganHandleFn({
            //   x:1,
            //   y:111
            // })
            // console.log(JSON.parse(JSON.stringify(loganHandle)))
            // loganHandleFn({
            //   x:2,
            //   y:2222
            // })

            // console.log(JSON.parse(JSON.stringify(loganHandle)))
            // loganHandleFn({
            //   x:3,
            //   y:3333
            // })
            // console.log(JSON.parse(JSON.stringify(loganHandle)))
            // loganHandleFn({
            //   x:4,
            //   y:4444
            // })
            // console.log(JSON.parse(JSON.stringify(loganHandle)))

            async function setup() {
                // Grab elements, create settings, etc.
                video = document.getElementById('video');
                canvas = document.getElementById('canvas1');
                ctx = canvas.getContext('2d');
                const stream = await navigator.mediaDevices.getUserMedia({ video: true });

                video.srcObject = stream;
                video.play();


                handpose = ml5.handpose(video, modelReady);

                // This sets up an event that fills the global variable "predictions"
                // with an array every time new hand poses are detected
                handpose.on("hand", results => {
                    hands = results;
                });

                // Hide the video element, and just show the canvas
                // video.hide();


                function modelReady() {
                    console.log("Model ready!");
                }

                requestAnimationFrame(draw);
            }

            setup();

            function modelReady() {
                console.log('model loaded!')
            }
            function drawKeypoints() {
                for (let i = 0; i < hands.length; i += 1) {
                    // hands一般来说都是单手的
                    const hand = hands[i];
                    /**
                     * @des 注册 
                      thumb: [1, 2, 3, 4],
                      indexFinger: [5, 6, 7, 8],
                      middleFinger: [9, 10, 11, 12],
                      ringFinger: [13, 14, 15, 16],
                      pinky: [17, 18, 19, 20],
                      palmBase: [0]
                     */
                    // for (let j = 1; j < hand.landmarks.length; j += 1)

                    
                    for (let j = 8; j < 9; j += 1) {
                        // 食指
                        let keypoint = hand.landmarks[j];
                        ctx.fillStyle = 'rgb(255, 215, 0)'
                        ctx.beginPath();
                        ctx.arc(keypoint[0], keypoint[1], 10, 0, 2 * Math.PI);
                        loganHandleFn({ x: keypoint[0], y: keypoint[1] })
                        ctx.fill()
                        // if(distance>15){
                        //   console.log(distance)
                        //   console.log("你在挥手")
                        //   speakbot.speak("你在挥手")
                        // }
                        // console.log(distance.x_distance,distance.y_distance)
                        if (distance.x_distance > 3) {
                            // console.log("横着走")
                            // speakbot.speak("横着走")
                            // window.scrollBy(document.body.clientWidth * (loganHandle.prev.x - loganHandle.next.x) / 1000, 0)
                            // model.position.z = model.position.z + (loganHandle.prev.x - loganHandle.next.x) /2
                            model.rotateY( (loganHandle.prev.x - loganHandle.next.x) /40);
                        }
                        if (distance.y_distance > 3) {
                            // console.log("竖着走")
                            // speakbot.speak("竖着走")
                            model.position.y = model.position.y + (loganHandle.prev.y - loganHandle.next.y) /2
                            // window.scrollBy(0, document.body.clientHeight * (loganHandle.prev.y - loganHandle.next.y) / 1000)
                        }
                        ctx.stroke();


                        
                       
                        
                        ctx.fill()
                        ctx.stroke();

                        // fill(0, 255, 0);
                        // noStroke();
                        // ellipse(keypoint[0], keypoint[1], 10, 10);
                    }
                }
            }

            function draw() {
                requestAnimationFrame(draw);
                // console.log(poses)
                ctx.drawImage(video, 0, 0, 640, 480);
                // We can call both functions to draw all keypoints and the skeletons
                if (hands && hands.length != 0) {
                    // console.log(hands)
                    drawKeypoints()
                }




                // For one pose only (use a for loop for multiple poses!)
                // if (poses.length > 0) {
                //   const pose = poses[0].pose;

                //   // Create a pink ellipse for the nose
                //   const nose = pose.nose;

                //   ctx.fillStyle = 'rgb(213, 0, 143)';
                //   ctx.beginPath();
                //   ctx.arc(nose.x, nose.y, 10, 0, 2 * Math.PI);
                //   ctx.fill();
                //   ctx.stroke();
                // }
            }

        }

    </script>
    <script type="module">
        document.querySelector("button").addEventListener('click', () => {
            // var actions =actions
            main(model,actions )
            // alert("ds")
            // for (var j = 0; j < actions.length; j++) {
            //     if (j == 16) {
            //         actions[j].play();
            //     }
            //     else {
            //         actions[j].stop();
            //     }
            // }

        })
        import * as THREE from 'three';
        import { GUI } from "gui";
        import { OrbitControls } from "OrbitControls"
        import { FBXLoader } from "FBXLoader"
        console.log(THREE)

        const clock = new THREE.Clock();
        let mixer
        let actions = []; //所有的动画数组
        function initModel(url) {
            return new Promise((resolve) => {
                new FBXLoader().load(url, (object) => {
                    mixer = new THREE.AnimationMixer(object);
                    for (let i in object.animations) {
                        actions[i] = mixer.clipAction(object.animations[i]);
                    }
                    // action = mixer.clipAction( object.animations[ 16 ] );
                    // console.log(object.animations)

                    object.traverse((res) => {
                        // 贴图
                        if (res.type == "Mesh") {
                            const textureUrl = ["https://cdn.jsdelivr.net/npm/electroluxasset/model/front_css_example/sphere/ref.jpg", "https://cdn.jsdelivr.net/npm/electroluxasset/model/front_css_example/sphere/Control_Module_nmap.jpg", "https://cdn.jsdelivr.net/npm/electroluxasset/model/front_css_example/sphere/walker_color.jpg"]
                            for (let i in textureUrl) {
                                const textureLoader = new THREE.TextureLoader();
                                const textureNormal = textureLoader.load(
                                    textureUrl[i]
                                );
                                res.material.map = textureNormal
                            }


                            // res.material.wireframe = true;

                        }

                    });
                    resolve(object);
                });
            });
        }

        let model = await initModel("https://cdn.jsdelivr.net/npm/electroluxasset/model/front_css_example/sphere/1.fbx");
        // model.setAnimation( actions[0].name );
        model.scale.set(0.25, 0.25, 0.25);

        // console.log(GUI)
        class threeInit {
            constructor(param) {
                this.param = {
                    x: 0,
                    y: 0,
                    z: 0
                }
                this.status = 16
                this.initScene()
                this.initLight()
                this.initObject()
                this.initCamera()
            }
            // 初始化物体
            initObject() {
                //材质
                // let material = new THREE.MeshLambertMaterial({
                //     color: 0x05c5cf4
                // });
                // let geometry = new THREE.BoxGeometry(100, 100, 100); //创建一个立方体几何对象Geometry
                // this.mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
                // this.mesh.position.set(this.param.x, this.param.y, this.param.z)

                // this.scene.add(this.mesh)
                this.mesh = model
                this.scene.add(model)
            }
            initScene() {
                this.scene = new THREE.Scene()
            }
            initLight() {
                let pointLight = new THREE.PointLight(0xffffff); //创建一个白色的点光源
                pointLight.position.set(0, 0, 150);
                this.scene.add(pointLight);
                let ambient = new THREE.AmbientLight(0xffffff, 1);
                this.scene.add(ambient);
                //                 const skyColor = 0xB1E1FF;  // bleu
                //   const groundColor = 0xB97A20;  // orange brun
                //   const intensity = 2;
                //                 const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
                //                 this.scene.add(light);
            }
            initCamera() {
                this.width = 1280; //canvas画布宽度
                this.height = 600; //canvas画布高度
                let k = this.width / this.height; //canvas画布宽高比
                //三维场景显示范围控制系数，系数越大，显示的范围越大
                //创建相机对象
                this.camera = new THREE.PerspectiveCamera(30, k, 20, 1000);
                this.camera.position.set(300, 600, 300); //设置相机位置
                let v1 = new THREE.Vector3(0, 0, 0)
                this.camera.lookAt(v1); //设置相机方向(指向的场景对象)
            }
            initGui(status) {

                //1.设置基本属性
                let gui = new GUI();
                gui.domElement.classList.add();
                gui.domElement.style.cssText = "position:absolute;top:0px;right:0px;";
                // 2.等于params.x的属性跟mesh.position.y绑定
                //-3 到 3 每一次0.01  mesh.position.x
                gui.add(this.param, 'x', -3, 100, 0.01)
                    .onChange(() => {
                        this.mesh.position.x = this.param.x;
                    }).name('x的位置')

                gui.add(this.param, 'y', -3, 100, 0.01)
                    .onChange(() => {
                        this.mesh.position.y = this.param.y;
                    }).name('y的位置(上下)')

                gui.add(this.param, 'z', -3, 100, 0.01)
                    .onChange(() => {
                        this.mesh.position.z = this.param.z;
                    }).name('z的位置')
                // gui.add(params, 'wireframe', [true, false])
                //     .onChange(function () {
                //         material.wireframe = params.wireframe;
                //     }).name('设置线框')


                // // gui.add(material, 'wireframe').name('设置线框')
                // // gui.add(params,'background').name('设置天空')
                // //gui.addColor
                // gui.addColor(params, 'color')
                //     .onChange(function () {
                //         material.color.set(params.color);
                //     });

                // //2.重要：实现切换天空的效果
                // gui.add(params, 'background', ["sky", "home"])
                //     .onChange(function () {
                //         initskyboxsun25deg(params.background);
                //     }).name("切换天空");
            }
            initSkyBox() {

                const starUrls = [
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/px.jpg"), // right
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/nx.jpg"), // left
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/py.jpg"), // top
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/ny.jpg"), // bottom
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/pz.jpg"), // back
                    ("https://cdn.jsdelivr.net/npm/electroluxasset/skybox/skyboxsun25deg/nz.jpg"), // front
                ];
                this.scene.background = new THREE.CubeTextureLoader().load(starUrls);


            }
            changeAction(status) {
                this.status = status
                for (var j = 0; j < actions.length; j++) {
                    if (j == status) {
                        actions[j].play();
                    }
                    else {
                        actions[j].stop();
                    }
                }

            }

            initRender(status) {

                let v1 = new THREE.Vector3(0, 0, 0)
                // let renderer = new THREE.WebGLRenderer();
                let renderer = new THREE.WebGLRenderer({ alpha: true });
                renderer.setSize(this.width, this.height);//设置渲染区域尺寸

                // 添加控制器
                let control = new OrbitControls(this.camera, renderer.domElement);
                control.target = v1 //设置相机方向(指向的场景对象)
                control.update();

                // renderer.setClearColor(0xb9d3ff, 1);
                // 渲染函数
                let render = () => {
                    // console.log(actions)
                    let status = this.status
                    // actions[status]?.play();
                    const delta = clock.getDelta();
                    if (mixer) mixer.update(delta);
                    control.update();
                    // this.mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
                    renderer.render(this.scene, this.camera);//执行渲染操作
                    // mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度
                    requestAnimationFrame(render);//请求再次执行渲染函数render，渲染下一帧
                }
                render(status);
                return renderer
            }


        }

        let three = new threeInit()
        three.initGui(1)

        // three.initSkyBox()
        let status = 16

        document.querySelector('.canvas').appendChild(three.initRender().domElement);




    </script>


</body>

</html>